home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Mine Sweeper / Source / mine.c < prev    next >
Text File  |  1994-04-26  |  13KB  |  599 lines

  1. /*    mine.c
  2.  *
  3.  *        How to handle the window for mining
  4.  */
  5.  
  6. #include <stdlib.h>
  7. #include "event.h"
  8. #include "mines.h"
  9.  
  10. #define        HEIGHT                20            /* Border height    */
  11.  
  12. /****************************************************************/
  13. /*                                                                */
  14. /*    Support Routines                                            */
  15. /*                                                                */
  16. /****************************************************************/
  17.  
  18. /*    DrawSICN
  19.  *
  20.  *        Draw this small icon
  21.  */
  22.  
  23. static void DrawSICN(Rect rr, short i)
  24. {
  25.     BitMap bmap;
  26.     Handle h;
  27.     GrafPtr foo;
  28.     Rect s;
  29.     
  30.     h = GetResource('SICN',128);
  31.     if (h == NULL) return;
  32.     HLock(h);
  33.     bmap.baseAddr = *h;
  34.     s.left = 0;
  35.     s.right = 16;
  36.     s.top = i * 16;
  37.     s.bottom = s.top + 16;
  38.     bmap.bounds = s;
  39.     bmap.bounds.top = 0;
  40.     bmap.rowBytes = 2;
  41.     
  42.     GetPort(&foo);
  43.     CopyBits(&bmap,&(foo->portBits),&s,&rr,0,NULL);
  44.     
  45.     HUnlock(h);
  46. }
  47.  
  48.  
  49. /*    GenBombs
  50.  *
  51.  *        This generates the bomb locations
  52.  */
  53.  
  54. void GenBombs(void)
  55. {
  56.     short i,x,y;
  57.     short a,b;
  58.     
  59.     for (x = 0; x < 40; x++) for (y = 0; y < 40; y++) {
  60.         BPosition[x][y] = 0;
  61.         BStatus[x][y] = 0;
  62.     }
  63.     for (i = 0; i < Bombs; i++) {
  64.         do {
  65.             x = Random() % SizeX;  if (x < 0) x += SizeX;
  66.             y = Random() % SizeY;  if (y < 0) y += SizeY;
  67.         } while (BPosition[x][y]);
  68.         BPosition[x][y] = -1;
  69.     }
  70.     for (x = 0; x < 40; x++) for (y = 0; y < 40; y++) {
  71.         if (BPosition[x][y] == -1) continue;
  72.         i = 0;
  73.         for (a = -1; a <= 1; a++) {
  74.             if (a + x < 0) continue;
  75.             if (a + x >= SizeX) continue;
  76.             for (b = -1; b <= 1; b++) {
  77.                 if (b + y < 0) continue;
  78.                 if (b + y >= SizeY) continue;
  79.                 if (BPosition[a+x][b+y] == -1) i++;
  80.             }
  81.         }
  82.         BPosition[x][y] = i;
  83.     }
  84. }
  85.  
  86. /*    FindLocation
  87.  *
  88.  *        This computes the location
  89.  */
  90.  
  91. Rect FindLocation(short x, short y)
  92. {
  93.     Rect r;
  94.     
  95.     r.left = x * 16 - 1;
  96.     r.top = y * 16 + HEIGHT;
  97.     r.right = r.left + 16;
  98.     r.bottom = r.top + 16;
  99.     return r;
  100. }
  101.  
  102.  
  103. /*    FillFailure
  104.  *
  105.  *        This fills in the failure states when this click fails
  106.  */
  107.  
  108. static void FillFailure(void)
  109. {
  110.     short a,b;
  111.     
  112.     for (a = 0; a < SizeX; a++) for (b = 0; b < SizeY; b++) {
  113.         if (BPosition[a][b] != -1) {
  114.             if (BStatus[a][b] == 1) BStatus[a][b] = 4;
  115.         } else {
  116.             if (BStatus[a][b] == 0) BStatus[a][b] = 1;
  117.             else if (BStatus[a][b] == 2) BStatus[a][b] = 1;
  118.         }
  119.         DrawSICN(FindLocation(a,b),BStatus[a][b]);
  120.     }
  121. }
  122.  
  123.  
  124.  
  125. /*    ClearPoint
  126.  *
  127.  *        Given a point, this attempts to handle the click in this point
  128.  */
  129.  
  130. static void ClearPoint(short i,short j, short flag)
  131. {
  132.     if (i < 0) return;
  133.     if (j < 0) return;
  134.     if (i >= SizeX) return;
  135.     if (j >= SizeY) return;
  136.     
  137.     if (BState) return;                        /* Ack!  Don't do anything        */
  138.     
  139.     if (BStatus[i][j] == 0) {                /* Status is cleared            */
  140.         if (BPosition[i][j] == -1) {        /* Position has bomb; die        */
  141.             BState = 1;
  142.             BStatus[i][j] = 5;
  143.             DrawSICN(FindLocation(i,j),BStatus[i][j]);
  144.             FillFailure();
  145.         } else if (BPosition[i][j] > 0) {    /* Position is not friendly        */
  146.             BStatus[i][j] = BPosition[i][j] + 5;
  147.             DrawSICN(FindLocation(i,j),BStatus[i][j]);
  148.             
  149.             if (Cruse) {
  150.                 short a,b;                    /* See if total marked are okay */
  151.                 short count;
  152.                 short n;
  153.                 
  154.                 /*
  155.                  *    Cruse control:  if I'm clicking on this cell, and the
  156.                  *    number of marked items match, then continue.
  157.                  */
  158.                 
  159.                 count = 0;
  160.                 for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  161.                     if (a + i < 0) continue;
  162.                     if (b + j < 0) continue;
  163.                     if (a + i >= SizeX) continue;
  164.                     if (b + j >= SizeY) continue;
  165.                     n = BStatus[a+i][b+j];
  166.                     
  167.                     if (n == 1) count++;
  168.                 }
  169.                 
  170.                 if (count == BPosition[i][j]) {    /* Count match */
  171.                     for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  172.                         if (a + i < 0) continue;
  173.                         if (b + j < 0) continue;
  174.                         if (a + i >= SizeX) continue;
  175.                         if (b + j >= SizeY) continue;
  176.                         if (BStatus[a+i][b+j] == 1) continue;
  177.                         
  178.                         ClearPoint(a+i,b+j,flag+1);
  179.                     }
  180.                 }
  181.             }
  182.         } else {                            /* Position is friendly.        */
  183.             short a,b;
  184.             BStatus[i][j] = 3;
  185.             DrawSICN(FindLocation(i,j),BStatus[i][j]);
  186.             for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  187.                 ClearPoint(i+a,b+j,flag+1);
  188.             }
  189.         }
  190.     } else if ((BStatus[i][j] >= 6) && (!flag)) {        /* Status has value        */
  191.         short num = 0;
  192.         short a,b;
  193.         short count = 0;
  194.         for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  195.             if ((a+i<0) || (b+j<0)) continue;
  196.             if ((a+i>=SizeX) || (b+j>=SizeY)) continue;
  197.             if (BStatus[a+i][b+j] == 1) num++, count++;
  198.             if (BStatus[a+i][b+j] == 2) count++;
  199.             if (BStatus[a+i][b+j] == 0) count++;
  200.         }
  201.         if (num < BPosition[i][j]) {
  202.             /*
  203.              *    Cruse Control #2:  If I click on a cell which has a match between
  204.              *    the number of unclicked objects and the number in the cell, then
  205.              *    this marks those objects
  206.              */
  207.             
  208.             if (Cruse && (count == BPosition[i][j])) {
  209.                 for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  210.                     if ((a+i<0) || (b+j<0)) continue;
  211.                     if ((a+i>=SizeX) || (b+j>=SizeY)) continue;
  212.                     if ((BStatus[a+i][b+j] == 2) || (BStatus[a+i][b+j] == 0)) {
  213.                         BStatus[a+i][b+j] = 1;
  214.                         DrawSICN(FindLocation(a+i,b+j),BStatus[a+i][b+j]);
  215.                     }
  216.                 }
  217.             }
  218.         } else {
  219.             for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  220.                 ClearPoint(a+i,b+j,flag+1);
  221.             }
  222.         }
  223.     }
  224. }
  225.  
  226. /*    ScanUniverse
  227.  *
  228.  *        This scans through my entire universe, calling ClearPoint over and over again
  229.  *    if any entry point with a count has the same number of uncovered or marked points
  230.  *    as the point provided.
  231.  */
  232.  
  233. static void ScanUniverse(void)
  234. {
  235.     short a,b;
  236.     short i,j;
  237.     short n,count;
  238.     short cover;
  239.     short nbomb;
  240.     
  241.     if (!Cruse) return;                        /* Cruse control is not on */
  242.     if (BState) return;                        /* The state routine fails */
  243.     
  244.     for (a = 0; a < SizeX; a++) for (b = 0; b < SizeY; b++) {
  245.         if (BStatus[a][b] < 6) continue;
  246.         count = 0;
  247.         cover = 0;
  248.         nbomb = 0;
  249.         for (i = -1; i <= 1; i++) for (j = -1; j <= 1; j++) {
  250.             if (a + i < 0) continue;
  251.             if (b + j < 0) continue;
  252.             if (a + i >= SizeX) continue;
  253.             if (b + j >= SizeY) continue;
  254.             n = BStatus[a+i][b+j];
  255.             if ((n == 0) || (n == 1) || (n == 2)) count++;
  256.             if (n == 0) cover++;
  257.             if (n == 1) nbomb++;
  258.         }
  259.         
  260.         if ((BPosition[a][b] == count) && (cover > 0)) {    /* Click if bomb is here */
  261.             ClearPoint(a,b,0);
  262.             a = 0;
  263.             b = -1;                            /* Force restart */
  264.         }
  265.         
  266.         if ((BPosition[a][b] == nbomb) && (cover > 0)) {    /* Click if nbombs same */
  267.             ClearPoint(a,b,0);
  268.             a = 0;
  269.             b = -1;                            /* Force restart */
  270.         }
  271.     }
  272. }
  273.  
  274.             
  275. /*    TestWin
  276.  *
  277.  *        Scan to see if I won
  278.  */
  279.  
  280. void TestWin()
  281. {
  282.     short a,b;
  283.     short i1;
  284.     Handle i2;
  285.     Rect i3;
  286.     
  287.     for (a = 0; a < SizeX; a++) for (b = 0; b < SizeY; b++) {
  288.         if (BStatus[a][b] == 0) return;
  289.         if (BStatus[a][b] == 2) return;            /* Fail */
  290.         if (BStatus[a][b] == 1) {
  291.             if (BPosition[a][b] == -1) continue;
  292.             return;
  293.         }
  294.         if (BStatus[a][b] == 3) continue;
  295.         if (BStatus[a][b] == 4) return;
  296.         if (BStatus[a][b] == 5) return;
  297.     }
  298.     
  299.     /*
  300.      *    If I made it here, there are no clears, no questions, and every
  301.      *    mark is correct, then I won
  302.      */
  303.     
  304.     SysBeep(5);
  305.     BState = 1;
  306.     BCount = 0;
  307.     
  308.     if ((SizeFlag != 2) || (BombFlag != 1)) return;
  309.     
  310.     for (a = 0; a < 4; a++) {
  311.         if (CurTime < Scores[a].time) break;
  312.     }
  313.     if (a == 4) return;
  314.     for (b = 3; b > a; b--) {
  315.         Scores[b] = Scores[b-1];
  316.     }
  317.     Scores[a].time = CurTime;
  318.     GetUserName(Scores[a].name);
  319. }
  320.  
  321. /****************************************************************************/
  322. /*                                                                            */
  323. /*    Window management routines                                                */
  324. /*                                                                            */
  325. /****************************************************************************/
  326.  
  327. /*    NewMines
  328.  *
  329.  *        This opens the mines window (if state is cleared)
  330.  */
  331.  
  332. void NewMines(void)
  333. {
  334.     Rect r;
  335.     short x = XLoc, y = YLoc;
  336.     
  337.     if (MineWindow != NULL) {
  338.         x = MineWindow->portRect.left - MineWindow->portBits.bounds.left;
  339.         y = MineWindow->portRect.top - MineWindow->portBits.bounds.top;
  340.     }
  341.     r.left = x;
  342.     r.right = r.left - 1 + 16 * SizeX;
  343.     r.top = y;
  344.     r.bottom = r.top + HEIGHT + 16 * SizeY;
  345.     
  346.     if (MineWindow == NULL) {
  347.         MineWindow = NewWindow(NULL,&r,"\pMines",1,4,(WindowPtr)-1L,1,0L);
  348.         SetPort(MineWindow);
  349.     } else {
  350.         SetPort(MineWindow);
  351.         EraseRect(&(MineWindow->portRect));
  352.         SizeWindow(MineWindow,r.right-r.left,r.bottom-r.top,0);
  353.         InvalRect(&(MineWindow->portRect));
  354.     }
  355.     ((WindowPeek)MineWindow)->windowKind = WK_GAME;
  356.     
  357.     Bombs = (SizeX * SizeY) / BRatio;    /* Bomb count ratio */
  358.     BState = 0;                            /* Start game over again */
  359.     BFirst = 1;
  360.     GenBombs();                            /* And generate some bombs */
  361.     LogTime = TickCount();                /* Store current time */
  362.     CurTime = -1;
  363. }
  364.  
  365. /*    DrawInteger
  366.  *
  367.  *        This draws the integer provided on the display at the location specified
  368.  */
  369.  
  370. void DrawInteger(short val, short px, short py)
  371. {
  372.     Rect r;
  373.     short x;
  374.     short y;
  375.     char buffer[32];
  376.     char *a;
  377.     
  378.     ForeColor(redColor);
  379.     r.top = py; r.bottom = r.top + 16;
  380.     r.left = px + 32; r.right = r.left + 16;
  381.     
  382.     NumToString(val,(unsigned char *)buffer);
  383.     y = buffer[0];
  384.     if (y <= 3) {
  385.         for (x = 0; x < y; x++) buffer[x] = buffer[x+1];
  386.         buffer[x] = '\0';
  387.     } else {
  388.         buffer[0] = '-';
  389.         buffer[1] = '-';
  390.         buffer[2] = '-';
  391.         buffer[3] = '\0';
  392.     }
  393.     for (a = buffer; *a != '\0'; a++);
  394.     y = 3;
  395.     while (--a >= buffer) {
  396.         if (*a == '-') DrawSICN(r,16);
  397.         else DrawSICN(r,17+(*a-'0'));
  398.         OffsetRect(&r,-16,0);
  399.     }
  400.     while (y >= 0) {
  401.         EraseRect(&r);
  402.         OffsetRect(&r,-16,0);
  403.         y--;
  404.     }
  405.     ForeColor(blackColor);
  406. }
  407.  
  408. /*    DoPeriodic
  409.  *
  410.  *        This handle periodic events.  The only thing that happens periodically
  411.  *    is the timer
  412.  */
  413.  
  414. void DoPeriodic(void)
  415. {
  416.     long time;
  417.     
  418.     if (BState) return;
  419.     
  420.     time = (TickCount() - LogTime)/60;
  421.     if (time == CurTime) return;
  422.     SetPort(MineWindow);
  423.     DrawInteger((short)(CurTime = time),(MineWindow->portRect.right)-50,2);
  424. }
  425.  
  426. /*    CountUpdate
  427.  *
  428.  *        Draw the score
  429.  */
  430.  
  431. void CountUpdate(void)
  432. {
  433.     short i,j;
  434.     
  435.     if (!BState) {
  436.         BCount = 0;
  437.         for (i = 0; i < SizeX; i++) for (j = 0; j < SizeY; j++) {
  438.             if (BPosition[i][j] == -1) BCount++;
  439.         }
  440.         for (i = 0; i < SizeX; i++) for (j = 0; j < SizeY; j++) {
  441.             if (BStatus[i][j] == 1) BCount--;
  442.         }
  443.     }
  444.     DrawInteger((long)BCount,2,2);
  445.     DrawInteger((short)CurTime,(MineWindow->portRect.right)-50,2);
  446. }
  447.  
  448.  
  449. /*    UpdateMines
  450.  *
  451.  *        This updates the mines display
  452.  */
  453.  
  454. void UpdateMines(WindowPtr w)
  455. {
  456.     short i,j;
  457.     short minx,maxx,miny,maxy;
  458.     Rect r;
  459.     
  460.     CountUpdate();
  461.     
  462.     /*
  463.      *    Draw the contents display
  464.      */
  465.     
  466.     r = (**(w->visRgn)).rgnBBox;
  467.     r.left = (r.left+1)/16;
  468.     r.right = (r.right/16)+1;
  469.     r.top = (r.top-HEIGHT+1)/16;
  470.     r.bottom = (r.bottom-HEIGHT+2)/16+1;
  471.     InsetRect(&r,-1,-1);                /* Paranoia--written at 12:45am */
  472.     if (r.left < 0) r.left = 0;
  473.     if (r.right > SizeX) r.right = SizeX;
  474.     if (r.top < 0) r.top = 0;
  475.     if (r.bottom > SizeY) r.bottom = SizeY;
  476.     for (i = r.left; i < r.right; i++) for (j = r.top; j < r.bottom; j++) {
  477.         DrawSICN(FindLocation(i,j),BStatus[i][j]);
  478.     }
  479. }
  480.  
  481. /*    ClickMines
  482.  *
  483.  *        What to do when the mouse goes down
  484.  */
  485.  
  486. void ClickMines(WindowPtr w, Point pt, short modifiers)
  487. {
  488.     Rect r,rr,rpos;
  489.     short i,j;
  490.     short tr[3][3];
  491.     short a,b;
  492.     short state;
  493.     
  494.     SetPort(w);
  495.     if (BState) return;                        /* No cigar                        */
  496.     
  497.     /*
  498.      *    Find where I've clicked, and change the state of the display
  499.      */
  500.     
  501.     i = (pt.h + 1)/16;
  502.     j = (pt.v - HEIGHT)/16;
  503.     if (j < 0) return;                        /* No click */
  504.     if (i < 0) i = 0;
  505.     if (i >= SizeX) i = SizeX;
  506.     if (j >= SizeY) j = SizeY;
  507.     rpos = FindLocation(i,j);
  508.     
  509.     /*
  510.      *    Handle shift key down
  511.      */
  512.     
  513.     if (modifiers & shiftKey) {
  514.         if (BStatus[i][j] == 0) BStatus[i][j] = 1;
  515.         else if (BStatus[i][j] == 1) BStatus[i][j] = 2;
  516.         else if (BStatus[i][j] == 2) BStatus[i][j] = 0;
  517.         DrawSICN(rpos,BStatus[i][j]);
  518.         CountUpdate();        
  519.         TestWin();
  520.         
  521.         return;
  522.     }
  523.     
  524.     /*
  525.      *    Handle until mouse down or up
  526.      */
  527.     
  528.     if (BStatus[i][j] == 0) {
  529.         for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  530.             if ((i+a<0) || (a+i>=SizeX)) continue;
  531.             if ((j+b<0) || (b+j>=SizeY)) continue;
  532.             tr[a+1][b+1] = BStatus[a+i][b+j];
  533.             tr[1][1] = 3;                            /* Empty */
  534.         }
  535.     } else if (BStatus[i][j] >= 6) {
  536.         for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  537.             if ((i+a<0) || (a+i>=SizeX)) continue;
  538.             if ((j+b<0) || (b+j>=SizeY)) continue;
  539.             tr[a+1][b+1] = BStatus[a+i][b+j];
  540.             if (BStatus[a+i][b+j] == 0) tr[a+1][b+1] = 3;
  541.             else if (BStatus[a+i][b+j] == 1) tr[a+1][b+1] = 5;
  542.         }
  543.     } else return;                                    /* Not accepted */
  544.  
  545.     state = 0;
  546.     while (Button()) {
  547.         GetMouse(&pt);
  548.         if (PtInRect(pt,&rpos)) {
  549.             if (!state) {
  550.                 state = 1;
  551.                 for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  552.                     if ((i+a<0) || (a+i>=SizeX)) continue;
  553.                     if ((j+b<0) || (b+j>=SizeY)) continue;
  554.                     DrawSICN(FindLocation(a+i,b+j),tr[a+1][b+1]);
  555.                 }
  556.             }
  557.         } else {
  558.             if (state) {
  559.                 state = 0;
  560.                 for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  561.                     if ((i+a<0) || (a+i>=SizeX)) continue;
  562.                     if ((j+b<0) || (b+j>=SizeY)) continue;
  563.                     DrawSICN(FindLocation(a+i,b+j),BStatus[a+i][b+j]);
  564.                 }
  565.             }
  566.         }
  567.     }
  568.  
  569.     /*
  570.      *    Now, put things back
  571.      */
  572.     
  573.     if (state) {
  574.         for (a = -1; a <= 1; a++) for (b = -1; b <= 1; b++) {
  575.             if ((i+a<0) || (a+i>=SizeX)) continue;
  576.             if ((j+b<0) || (b+j>=SizeY)) continue;
  577.             DrawSICN(FindLocation(a+i,b+j),BStatus[a+i][b+j]);
  578.         }
  579.     } else return;                                /* Don't do it! */
  580.     
  581.     /*
  582.      *    Figure out what has happened
  583.      */
  584.     
  585.     if ((BFirst) && (BPosition[i][j] == -1)) {
  586.         BStatus[i][j] = 1;
  587.         DrawSICN(FindLocation(i,j),1);
  588.     } else {
  589.         ClearPoint(i,j,0);
  590.         ScanUniverse();                            /* Try to clear as much as I can */
  591.     }
  592.     
  593.     BFirst = 0;
  594.     CountUpdate();    
  595.     TestWin();
  596. }
  597.  
  598.  
  599.